`timescale 1ps/1ps
module cmos_tb;

// ========================================================================== //
// Parameters                                                                 //
// ========================================================================== //
    parameter SYS_CLK_PERIOD        = 50000;  // 20MHz
    parameter C1_NUM_DQ_PINS        = 16;
    parameter C1_MEM_ADDR_WIDTH     = 13;
    parameter C1_MEM_BANKADDR_WIDTH = 2;
    parameter EBI_ADDR_WIDTH        = 26;
    parameter EBI_DATA_WIDTH        = 16;
    parameter H_SIZE                = 320;
    parameter V_SIZE                = 240;
    parameter BRAM_X_BASE           = 0;
    parameter BRAM_Y_BASE           = H_SIZE*V_SIZE;//26'h1_2C00; // 320x240
    parameter FPGA_REG_BASE         = {{(EBI_ADDR_WIDTH-5){1'b1}},5'h0};
    parameter GUI_0_BASE            = 26'h2_5800;
    parameter GUI_1_BASE            = 26'h5_dc00;
    parameter DDR_BUF0_ADDR         = 30'h0;
    parameter DDR_BUF1_ADDR         = 30'h1_2C00;
    parameter RUN_TIME              = 64'd4_500_000_000;
// ========================================================================== //
// Signal Declarations                                                        //
// ========================================================================== //

// Clocks
reg                              rst;
reg                              c1_sys_clk;
wire                             sys_rst;

wire [C1_MEM_ADDR_WIDTH-1:0]     mcb1_dram_a;
wire [C1_MEM_BANKADDR_WIDTH-1:0] mcb1_dram_ba;
wire                             mcb1_dram_ck;
wire                             mcb1_dram_ck_n;
wire [C1_NUM_DQ_PINS-1:0]        mcb1_dram_dq;
wire                             mcb1_dram_dqs;
wire                             mcb1_dram_dqs_n;
wire                             mcb1_dram_dm;
wire                             mcb1_dram_ras_n;
wire                             mcb1_dram_cas_n;
wire                             mcb1_dram_we_n;
wire                             mcb1_dram_cke;
wire				             mcb1_dram_odt;
wire                             mcb1_dram_udqs;    // for X16 parts
wire                             mcb1_dram_udqs_n;  // for X16 parts
wire                             mcb1_dram_udm;     // for X16 parts

wire [EBI_DATA_WIDTH-1:0]       ebi_d;
wire [EBI_ADDR_WIDTH-1:0]       ebi_a;
wire                            ebi_ncs;
wire                            ebi_nrd;
wire                            ebi_nwe;

wire                            ddr_cal_done;
wire [1:0]                      x_done;

wire [7:0]                      cam_x_d;
wire                            cam_x_vsync;
wire                            cam_x_hsync;
wire                            cam_x_de;
wire                            cam_x_pclk;

wire [7:0]                      cam_y_d;
wire                            cam_y_vsync;
wire                            cam_y_hsync;
wire                            cam_y_de;
wire                            cam_y_pclk;

integer                         fp;
integer                         fp_x_buffer;
integer                         fp_y_buffer;
integer                         fp_gui0;
integer                         fp_gui1;

// ========================================================================== //
// Clocks Generation                                                          //
// ========================================================================== //
    // 20MHz
   initial
      c1_sys_clk = 1'b0;
   always
      #(SYS_CLK_PERIOD/2) c1_sys_clk = ~c1_sys_clk;

initial
begin
    rst = 1'b1;
    #(1000);
    @(negedge c1_sys_clk);
    rst = 1'b0;
end
        
    
assign sys_rst = u0.c1_rst0;
assign ddr_cal_done = u0.design_top.c1_calib_done;
assign x_done = u0.x_buf_done;

// The PULLDOWN component is connected to the ZIO signal primarily to avoid the
// unknown state in simulation. In real hardware, ZIO should be a no connect(NC) pin.
   wire mcb1_zio;
   wire mcb1_rzq;
   PULLDOWN zio_pulldown1 (.O(mcb1_zio));   PULLDOWN rzq_pulldown1 (.O(mcb1_rzq));

// CMOS tests
initial
begin
    wait(sys_rst==1'b0);
    wait(ddr_cal_done);
    sensor_x.OV7221_OUT_TASK("gray_640x480_2.txt");
end

initial
begin
    wait(sys_rst==1'b0);
    wait(ddr_cal_done);
    sensor_y.OV7221_OUT_TASK("gray_640x480.txt");
end

initial
begin
    fp = $fopen("rgb_240x320.txt","r");
    wait(sys_rst==1'b0);
    // disp mode
    u_ebi.EBI_TASK_WR_WORD(FPGA_REG_BASE+5'h0,16'd5); // 0=gui; 1=gui+x; 2 = gui+y; 3 = gui + x + y
    // offset_h
    u_ebi.EBI_TASK_WR_WORD(FPGA_REG_BASE+5'h2,16'd300);
    // offset_v
    u_ebi.EBI_TASK_WR_WORD(FPGA_REG_BASE+5'h4,16'd100);
    // gui_id
    u_ebi.EBI_TASK_WR_WORD(FPGA_REG_BASE+5'h6,16'd0);
    wait(ddr_cal_done);
    u0.gui_wr.MCB_TASK_WR_FILE(fp,GUI_0_BASE);
end

integer fp3,fp_x,fp_y,fp_xy;
initial
begin
    fp3 = $fopen("rgb_output.txt","w");
    // fp_x = $fopen("cmos_x.txt","w");
    // fp_y = $fopen("cmos_y.txt","w");
    // fp_xy= $fopen("cmos_xy.txt","w");
    //#(RUN_TIME);
    wait(ddr_cal_done);
    @(negedge u0.u_lcd.lcd_vsync);
    // $fclose(fp_x);
    // $fclose(fp_y);
    // $fclose(fp_xy);
    $fclose(fp3);
    $stop;
end

/* 
always@(posedge u0.cmos_x.cmos_wr_clk )
begin
    if(u0.cmos_x.cmos_wr_en)
    begin
        $fdisplay(fp_x,"%08x",u0.cmos_x.cmos_wr_data);
    end
end

always@(posedge u0.cmos_y.cmos_wr_clk )
begin
    if(u0.cmos_y.cmos_wr_en)
    begin
        $fdisplay(fp_y,"%08x",u0.cmos_y.cmos_wr_data);
    end
end

always@(posedge u0.u_lcd.clk )
begin
    if(u0.u_lcd.cmos_fifo_wr)
    begin
        $fdisplay(fp_xy,"%08x",u0.u_lcd.ddr_rd_data);
    end
end
 */
always@(posedge u0.u_lcd.lcd_pclk)
begin
    if(u0.u_lcd.lcd_de)
    begin
        $fdisplay(fp3,"%02x",u0.u_lcd.lcd_r);
        $fdisplay(fp3,"%02x",u0.u_lcd.lcd_g);
        $fdisplay(fp3,"%02x",u0.u_lcd.lcd_b);
    end
end

// ========================================================================== //
// DESIGN TOP INSTANTIATION                                                    //
// ========================================================================== //

arm_ebi_master # (
    .ADDR_WIDTH(EBI_ADDR_WIDTH),
    .DATA_WIDTH(EBI_DATA_WIDTH)
)
u_ebi
(
    .ebi_d      (ebi_d),
    .ebi_a      (ebi_a),
    .ebi_ncs    (ebi_ncs),
    .ebi_nrd    (ebi_nrd),
    .ebi_nwe    (ebi_nwe)
);

ov7221 sensor_x
(
    .data       (cam_x_d),
    .v_sync     (cam_x_vsync),
    .h_sync     (cam_x_hsync),
    .href       (cam_x_de),
    .pclk       (cam_x_pclk)
);

ov7221 sensor_y
(
    .data       (cam_y_d),
    .v_sync     (cam_y_vsync),
    .h_sync     (cam_y_hsync),
    .href       (cam_y_de),
    .pclk       (cam_y_pclk)
);

jrmoc_cmos # (
    .SIMULATION("TRUE")
) u0 (
    .clk                (c1_sys_clk),            // external 20MHz
    .rst                (rst),
    // ARM EBI interface
    .ebi_d              (ebi_d),
    .ebi_a              (ebi_a),
    .ebi_ncs            (ebi_ncs),
    .ebi_nrd            (ebi_nrd),
    .ebi_nwe            (ebi_nwe),

    // CAM_X
    .cam_x_mclk         (),
    .cam_x_d            (cam_x_d),
    .cam_x_vsync        (cam_x_vsync),
    .cam_x_hsync        (cam_x_hsync),
    .cam_x_href         (cam_x_de),
    .cam_x_pclk         (cam_x_pclk),

    // CAM_Y
    .cam_y_mclk         (),
    .cam_y_d            (cam_y_d),
    .cam_y_vsync        (cam_y_vsync),
    .cam_y_hsync        (cam_y_hsync),
    .cam_y_href         (cam_y_de),
    .cam_y_pclk         (cam_y_pclk),

    // LCD
    .lcd_r              (),
    .lcd_g              (),
    .lcd_b              (),
    .lcd_spclk          (),
    .lcd_de             (),
    .lcd_hsync          (),
    .lcd_vsync          (),

    // DDR2
    .f_ddr_d            (mcb1_dram_dq),
    .f_ddr_a            (mcb1_dram_a),
    .f_ddr_cke          (mcb1_dram_cke),
    .f_ddr_we           (mcb1_dram_we_n),
    .f_ddr_ba           (mcb1_dram_ba),
    .f_ddr_clk          (mcb1_dram_ck),
    .f_ddr_nclk         (mcb1_dram_ck_n),
    .f_ddr_ras          (mcb1_dram_ras_n),
    .f_ddr_cas          (mcb1_dram_cas_n),
    .f_ddr_odt          (mcb1_dram_odt),
    .f_ddr_udqs         (mcb1_dram_udqs),
    .f_ddr_udqsn        (mcb1_dram_udqs_n),
    .f_ddr_ldqs         (mcb1_dram_dqs),
    .f_ddr_ldqsn        (mcb1_dram_dqs_n),
    .f_ddr_udqm         (mcb1_dram_udm),
    .f_ddr_ldqm         (mcb1_dram_dm),
    .mcb1_rzq           (mcb1_rzq),
    .mcb1_zio           (mcb1_zio),
    .f_ddr_vref         ()
);

// ========================================================================== //
// Memory model instances                                                     //
// ========================================================================== //

ddr2_model_c1 u_mem_c1(
   .ck         (mcb1_dram_ck),
   .ck_n       (mcb1_dram_ck_n),
   .cke        (mcb1_dram_cke),
   .cs_n       (1'b0),
   .ras_n      (mcb1_dram_ras_n),
   .cas_n      (mcb1_dram_cas_n),
   .we_n       (mcb1_dram_we_n),
   .dm_rdqs    ({mcb1_dram_udm,mcb1_dram_dm}),
   .ba         (mcb1_dram_ba),
   .addr       (mcb1_dram_a),
   .dq         (mcb1_dram_dq),
   .dqs        ({mcb1_dram_udqs,mcb1_dram_dqs}),
   .dqs_n      ({mcb1_dram_udqs_n,mcb1_dram_dqs_n}),
   .rdqs_n     (),
   .odt        (mcb1_dram_odt)
 );

endmodule
